timeConversion.ts ➔ updateTimestamps   B
last analyzed

Complexity

Conditions 6

Size

Total Lines 28
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 24
dl 0
loc 28
rs 8.3706
c 0
b 0
f 0
1
import spacetime from 'spacetime';
2
import { getGlobalConfiguration, SETTINGS_websiteAutoTimeConversion } from '../configuration/configuration';
3
import * as core from '../utils/aniwatchCore';
4
import * as helper from '../utils/helpers';
5
6
const __alteredNodes = [];
7
const DAYS = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
8
9
export function init(): void {
10
    getGlobalConfiguration().getProperty(SETTINGS_websiteAutoTimeConversion, value => {
11
        if (value) {
12
            // The regexp pattern matches anything except the airing page. 
13
            // This is because we would have to restructure the complete site to update time data.
14
            // Additionally, there is a big hint that all data would be UTC+1
15
            core.runAfterLoad(() => {
16
                updateTimestamps(document.documentElement);
17
            }, "^/(?!airing).*$");
18
19
            core.runAfterLocationChange(() => {
20
                updateTimestamps(document.documentElement);
21
            }, "^/(?!airing).*$");
22
23
            core.registerScript((node: Node) => {
24
                updateTimestamps(node);
25
            }, "^/(?!airing).*$");
26
        }
27
    });
28
}
29
30
function getSpaceDateTimeFormat(use24Format: boolean): string {
31
    return `${getSpaceDateFormat()} ${getSpaceTimeFormat(use24Format)}`;
32
}
33
34
function getSpaceTimeFormat(use24Format: boolean): string {
35
    if (use24Format) {
36
        return '{time-24}';
37
    }
38
39
    return '{time}';
40
}
41
42
function getSpaceDateFormat(): string {
43
    return '{date}. {month-short} {year}';
44
}
45
46
function tryUpdateDateTime(node: Node): boolean {
47
    const REG_DATETIME = /(\d{2}(\/|\.)){2}\d{4} *\d?\d:\d{2}( (AM|PM))?/g;
48
    const REG_TIME = /\d?\d:\d{2}/;
49
    const REG_AMPM = /\s(am|pm)/i;
50
51
    let hits = Array.from(node.textContent.matchAll(REG_DATETIME), match => match[0]);
52
53
    if (hits.length === 0) {
54
        return false;
55
    }
56
57
    hits.forEach(hit => {
58
        let use24Format = false;
59
        let processedStr = hit
60
61
        // string must be converted into 12h format
62
        if (processedStr.search(REG_AMPM) < 0) {
63
            let timeStr = processedStr.match(REG_TIME)[0];
64
            let hm = timeStr.split(':');
65
            let hour = parseInt(hm[0]);
66
67
            if (hour >= 12) {
68
                timeStr = timeStr.replace(`${hour}:`, `${hour - 12}:`);
69
                timeStr += 'pm';
70
            }
71
            else {
72
                timeStr += 'am';
73
            }
74
75
            processedStr = processedStr.replace(REG_TIME, timeStr);
76
            use24Format = true;
77
        }
78
79
        // if time has a space before am/pm, this has to be removed for spacetime
80
        processedStr = processedStr.replace(REG_AMPM, '$1');
81
82
        let datetime = spacetime(processedStr, 'UTC+1', { dmy: true });
83
        datetime = datetime.goto(spacetime().tz);
84
        let replaceText = String(datetime.format(getSpaceDateTimeFormat(use24Format)));
85
86
        node.textContent = node.textContent.replace(hit, replaceText);
87
    });
88
89
    return true;
90
}
91
92
function tryUpdateDate(node: Node): boolean {
93
    const REG_DATE = /(\d{2}(\/|\.)){2}\d{4}/g;
94
95
    let hits = Array.from(node.textContent.matchAll(REG_DATE), match => match[0]);
96
97
    if (hits.length === 0) {
98
        return false;
99
    }
100
101
    hits.forEach(hit => {
102
        let datetime = spacetime(hit, 'UTC+1', { dmy: true });
103
        datetime = datetime.goto(spacetime().tz);
104
        let replaceText = String(datetime.format(getSpaceDateFormat()));
105
106
        node.textContent = node.textContent.replace(hit, replaceText);
107
    });
108
109
    return true;
110
}
111
112
function tryUpdateTime(node: Node): boolean {
113
    const REG_TIME = /\d?\d:\d{2}( (AM|PM))?/g;
114
    const REG_AMPM = /\s(am|pm)/i;
115
116
    let hits = Array.from(node.textContent.matchAll(REG_TIME), match => match[0]);
117
118
    if (hits.length === 0) {
119
        return false;
120
    }
121
122
    hits.forEach(hit => {
123
        let use24Format = false;
124
        let processedStr = hit
125
126
        // string must be converted into 12h format
127
        if (processedStr.search(REG_AMPM) < 0) {
128
            let timeStr = processedStr.match(REG_TIME)[0];
129
            let hm = timeStr.split(':');
130
            let hour = parseInt(hm[0]);
131
132
            if (hour >= 12) {
133
                timeStr = timeStr.replace(`${hour}:`, `${hour - 12}:`);
134
                timeStr += 'pm';
135
            }
136
            else {
137
                timeStr += 'am';
138
            }
139
140
            processedStr = processedStr.replace(REG_TIME, timeStr);
141
            use24Format = true;
142
        }
143
144
        // if time has a space before am/pm, this has to be removed for spacetime
145
        processedStr = processedStr.replace(REG_AMPM, '$1');
146
147
        let datetime = spacetime();
148
        datetime = datetime.goto('UTC+1');
149
        datetime = datetime.time(processedStr);
150
        datetime = datetime.goto(spacetime().tz);
151
        let replaceText = String(datetime.format(getSpaceTimeFormat(use24Format)));
152
153
        node.textContent = node.textContent.replace(hit, replaceText);
154
155
        // SPECIAL CASE: Anime has the day written in broadcast bade. This may be different in another timezone
156
        let tzMeta = spacetime().timezone();
157
        let originalH = datetime.hour() - tzMeta.current.offset + 1;
158
159
        let dOffset = 0;
160
        // we moved to next day
161
        if (originalH < 0) {
162
            dOffset = 1;
163
        }
164
        // we moved to previous day
165
        else if (originalH > 24) {
166
            dOffset = -1;
167
        }
168
169
        // if day changed
170
        if (dOffset != 0) {
171
            let dayNode = (node.parentNode as Element)?.previousElementSibling;
172
            if (helper.assigned(dayNode)) {
173
                for (let i = 0; i < DAYS.length; i++) {
174
                    if (dayNode.textContent.indexOf(DAYS[i]) >= 0) {
175
                        dayNode.textContent = dayNode.textContent.replace(DAYS[i], DAYS[(i + DAYS.length + dOffset) % DAYS.length]); // add DAYS.length to avoid negative numbers in the modulo operation
176
                        break;
177
                    }
178
                }
179
            }
180
        }
181
    });
182
183
    return true;
184
}
185
186
function tryUpdateTimeZone(node: Node): boolean {
187
    const HINT_UTC = 'UTC+1';
188
    if (node.textContent === HINT_UTC) {
189
        let tzMeta = spacetime().timezone();
190
191
        node.textContent = `${tzMeta.name} (UTC${tzMeta.current.offset >= 0 ? '+' : ''}${tzMeta.current.offset})`;
192
193
        return true;
194
    }
195
196
    return false;
197
}
198
199
function updateTimestamps(node) {
200
    let nodes = helper.findTextNodes(node);
201
202
    nodes.forEach(node => {
203
        // avoid double updates
204
        if (__alteredNodes.indexOf(node) >= 0) {
205
            return;
206
        }
207
208
        if (tryUpdateDateTime(node)) {
209
            __alteredNodes.push(node);
210
            return;
211
        }
212
213
        if (tryUpdateDate(node)) {
214
            __alteredNodes.push(node);
215
            return;
216
        }
217
218
        if (tryUpdateTime(node)) {
219
            __alteredNodes.push(node);
220
            return;
221
        }
222
223
        if (tryUpdateTimeZone(node)) {
224
            __alteredNodes.push(node);
225
            return;
226
        }
227
    });
228
}